package com.gitee.beiding.cloud.easy_call.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.gitee.beiding.cloud.easy_call.entity.BeanWrapper;
import com.gitee.beiding.cloud.easy_call.entity.InvokeWrapper;
import com.gitee.beiding.cloud.easy_call.entity.ResultWrapper;
import com.gitee.beiding.cloud.easy_call.exception.CannotGetMethodException;
import com.gitee.beiding.cloud.easy_call.exception.CannotObtainBeanException;
import com.google.common.base.Joiner;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Map;

@RestController
public class InvokedController implements ApplicationContextAware {


    private Map<String, BeanWrapper> beanWrapperMap;

    private ObjectMapper objectMapper = new ObjectMapper();


    private String methodName(String name, List<String> params) {
        return name + "(" + Joiner.on(",").join(params) + ")";
    }

    @PostMapping("/easy-call")
    ResultWrapper call(@RequestBody InvokeWrapper invokeWrapper) {

        ResultWrapper resultWrapper = new ResultWrapper();

        String name = invokeWrapper.getName();
        BeanWrapper beanWrapper;
        if (name == null || name.equals("") || (beanWrapper = beanWrapperMap.get(name)) == null) {
            resultWrapper.setExceptionClassName(CannotObtainBeanException.class.getName());
            resultWrapper.setExceptionMsg("无法找到对应的Bean:" + invokeWrapper.getName());
            return resultWrapper;
        }

        //确保参数不为空
        if (invokeWrapper.getParamTypeNames() == null) {
            invokeWrapper.setParamTypeNames(Collections.emptyList());
        }

        if (invokeWrapper.getParamJsonObjects() == null) {
            invokeWrapper.setParamJsonObjects(Collections.emptyList());
        }

        Method method;
        if (invokeWrapper.getMethodName() == null || invokeWrapper.getParamJsonObjects().size() != invokeWrapper.getParamTypeNames().size() || (method = beanWrapper.getMethod(invokeWrapper.getMethodName(), invokeWrapper.getParamTypeNames())) == null) {
            resultWrapper.setExceptionClassName(CannotGetMethodException.class.getName());
            resultWrapper.setExceptionMsg("无法找到对应方法:" + methodName(invokeWrapper.getMethodName(), invokeWrapper.getParamTypeNames()));
            return resultWrapper;
        }


        List<String> paramJsonObjects = invokeWrapper.getParamJsonObjects();
        Class[] paramsClasses = method.getParameterTypes();

        //准备所有参数
        Object[] params = new Object[paramJsonObjects.size()];

        for (int i = 0; i < paramJsonObjects.size(); i++) {
            try {
                Class aClass = paramsClasses[i];
                if (aClass == String.class) {
                    params[i] = paramJsonObjects.get(i);

                } else {
                    Object o = objectMapper.readValue(paramJsonObjects.get(i), SimpleType.constructUnsafe(aClass));
                    params[i] = o;
                }

            } catch (Exception e) {
                resultWrapper.setExceptionClassName(CannotGetMethodException.class.getName());
                resultWrapper.setExceptionMsg("参数实例化失败:" + invokeWrapper.getMethodName());
                return resultWrapper;
            }
        }

            try {
                Object invoke = method.invoke(beanWrapper.getBean(), params);
                resultWrapper.setReturnResult(objectMapper.writeValueAsString(invoke));
                return resultWrapper;
            } catch (Exception e) {
                resultWrapper.setExceptionClassName(CannotGetMethodException.class.getName());
                resultWrapper.setExceptionMsg("方法调用失败:" + invokeWrapper.getMethodName());
                return resultWrapper;
            }

    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.beanWrapperMap = applicationContext.getBean("_easyCall_beanWrapperMap", Map.class);
    }
}
